#include "inode.h"
#include "fs.h"
#include "file.h"
#include "global.h"
#include "debug.h"
#include "memory.h"
#include "interrupt.h"
#include "list.h"
#include "stdio-kernel.h"
#include "string.h"
#include "super_block.h"

// 用来存储 inode 的位置信息
struct inode_position {
    bool two_sec;       // inode 是否跨扇区
    uint32_t sec_lba;   // inode 所在的扇区号
    uint32_t off_size;  // inode 所在扇区内的字节偏移量
};

// 根据 inode 编号获取 inode 所在扇区和扇区内的偏移量
static void inode_locate(struct partition* part, uint32_t inode_no, struct inode_position* inode_pos) {
    // inode_table 在硬盘上是连续的
    ASSERT(inode_no < 4096); 

    uint32_t inode_table_lba = part -> sb -> inode_table_lba;
    uint32_t inode_size = sizeof(struct inode);
    uint32_t off_size = inode_no * inode_size; // 第 inode_no 号 inode 相对于 inode_table_lba 偏移多少字节
    uint32_t off_sec = off_size / 512; // 第 inode_no 号 inode 相对于 inode_table_lba 偏移多少扇区
    uint32_t off_size_in_sec = off_size % 512; // 第 inode_no 号 inode 在扇区内的字节偏移

    // 判断此 inode 是否跨越两个扇区
    uint32_t left_in_sec = 512 - off_sec;
    if(left_in_sec < inode_size) // 若扇区内剩余的空间不足以容纳一个 inode，则 inode 必然跨越了两个扇区
        inode_pos -> two_sec = true;
    else
        inode_pos -> two_sec = false;
    inode_pos -> sec_lba = inode_table_lba + off_sec;
    inode_pos -> off_size = off_size_in_sec;
}

// 将 inode 写入到分区 part 中 —— 同步
void inode_sync(struct partition* part, struct inode* inode, void* io_buf) {
    uint8_t inode_no = inode -> i_no;

    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos); // 将 inode 的位置信息保存到 inode_pos

    ASSERT(inode_pos.sec_lba <= (part -> start_lba + part -> sec_cnt));

    /**
     * 硬盘中 inode 的成员 inode_tag 和 i_open_cnts 
     * 它们只在内存中记录链表位置和被多少进程所共享
     * 因此在写入硬盘前，需要对其清理
     */
    // 不对现有的 inode 进行清理，而是复制一个 inode 进行清理，最后同步这个复制的 inode
    struct inode pure_inode;
    memcpy(&pure_inode, inode, sizeof(struct inode));

    // 进行清理
    pure_inode.i_open_cnt = 0;
    pure_inode.write_deny = false;
    pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL;

    char* inode_buf = (char*) io_buf;
    if(inode_pos.two_sec) { // 处理两个扇区
        // 先读取两个扇区
        ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
        // 将 pure_inode 复制到 (inode_buf + inode_pos.off_size)
        memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
        // 写入两个扇区
        ide_write(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
    } else { // 处理一个扇区
        ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
        memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
        ide_write(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
    }
}

// 根据 inode 编号返回相应的 inode 实体
struct inode* inode_open(struct partition* part, uint32_t inode_no) {
    // 先在已打开的 inode 中查找
    struct list_elem* elem = part -> open_inodes.head.next;
    struct inode* inode_found;
    while(elem != &part -> open_inodes.tail) {
        inode_found = elem2entry(struct inode, inode_tag, elem);
        if(inode_found -> i_no == inode_no) {
            inode_found -> i_open_cnt++;
            return inode_found;
        }
        elem = elem -> next;
    }

    // --------------------------------------------------------
    // 在已有的找不到，则从硬盘查找，并且加入到 part -> open_inodes 中
    // --------------------------------------------------------
    // 得到 inode 信息
    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos);

    /**
     * 为了使 sys_malloc 创建的新 inode 被所有任务所共享
     * 需要将 inode 置于内核空间
     * 则需要临时将 cur_pbc -> pgdir = NULL
     */
    struct task_struct* cur = running_thread();
    uint32_t* cur_pagedir_bak = cur -> pgdir;
    cur -> pgdir = NULL;
    // cur -> pgdir = NULL 后，此时申请的就是内核空间
    inode_found = (struct inode*) sys_malloc(sizeof(struct inode));
    // 恢复 pgdir
    cur -> pgdir = cur_pagedir_bak;

    char* inode_buf;
    if(inode_pos.two_sec) { // 跨两个扇区
        inode_buf = (char*) sys_malloc(1024);
        ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 2);
    } else { // 没跨扇区
        inode_buf = (char*) sys_malloc(512);
        ide_read(part -> my_disk, inode_pos.sec_lba, inode_buf, 1);
    }

    memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));

    list_push(&part -> open_inodes, &inode_found -> inode_tag);
    inode_found -> i_open_cnt = 1; // 打开了一次

    sys_free(inode_buf);
    return inode_found;
}

// 关闭 inode 或减少 inode 的打开次数
void inode_close(struct inode* inode) {
    enum intr_status old_status = intr_disable();
    if(--inode -> i_open_cnt == 0) {
        list_remove(&inode -> inode_tag);
        /**
         * inode_open 时为实现 inode 被所有进程所共享
         * 已经为 inode 分配了内核空间的资源
         * 所以此时释放也需要释放内核空间的资源
        */
        struct task_struct* cur = running_thread();
        uint32_t* cur_pagedir_bak = cur -> pgdir;
        cur -> pgdir = NULL;
        sys_free(inode);
        cur -> pgdir = cur_pagedir_bak;
    }
    intr_set_status(old_status);
}

// 初始化 inode
void inode_init(uint32_t inode_no, struct inode* new_inode) {
    new_inode -> i_no = inode_no;
    new_inode -> i_size = 0;
    new_inode -> i_open_cnt = 0;
    new_inode -> write_deny = false;

    uint8_t sec_idx = 0;
    while(sec_idx < 13) { // 初始化直接块(0~11)，索引 >= 12 就是间接块
        new_inode -> i_sectors[sec_idx] = 0;
        sec_idx++;
    }
}
